`include "PRV564Config.v"
`include "PRV564Define.v"
//////////////////////////////////////////////////////////////////////////////////////////////////
//  Date    : 2021                                                                              //
//  Author  : Jack.Pan                                                                          //
//  Desc    : TLB For PRV564 processor                                                          //
//  Version : 0.0(Orignal)                                                                      //
//////////////////////////////////////////////////////////////////////////////////////////////////
module TLB
#(
    parameter TLB_entry_NUM = 16
)(
    //Global Signals
    input wire              TLBi_CLK,
    input wire              TLBi_ARST,
    input wire              TLBi_ModifyPermit,      //允许模块进行内容修改
    input wire              TLBi_pipflush,
    input wire              TLBi_refersh,           //(From Master) Request refersh TLB
    input wire [1:0]        TLBi_privilege,
    input wire              TLBi_CSR_mxr,
    input wire              TLBi_CSR_sum,
//---------------------TLB access signals---------------------
    input  wire             TLBi_valid,         //操作有效
    input  wire [7:0]       TLBi_Opcode,        //8bit opcode
    input  wire [`XLEN-1:0] TLBi_DATA_VA,       //虚拟地址输入
//---------------------TLB output----------------------------
    output  reg             TLBo_valid,
    output  reg             TLBo_LoadPageFlt,
    output  reg             TLBo_StorePageFlt,
    output  reg             TLBo_InstPageFlt,
    output  reg [`XLEN-1:0] TLBo_DATA_PA,        //物理地址输出
    output  reg             TLBo_DATA_Cacheable, //可以被缓存
    output  reg             TLBo_DATA_WrThrough, //此地址需要被写穿透
//----------------------Connect to PTW----------------------
    output reg [7:0]        PTWi_CMD,           //command 
    output reg              PTWi_V,             //Valid
    output reg [3:0]        PTWi_WalkID,        //Current translation ID
    output reg [26:0]       PTWi_VPN,           //Virtual Address Input
    input wire              PTWo_FULL,          //PTW is full!
//---------PTE and PPN Reply-----------------
    input wire [43:0]       PTWo_PPN,           //Physical Page Number Output(After Translation)
    input wire [7:0]        PTWo_RPL,
    input wire [3:0]        PTWo_WalkID,
    input wire              PTWo_V,
    input wire [9:0]        PTWo_PTE,           //Page Table Entry
    input wire [1:0]        PTWo_PageSize,      //Page size 2:1G, 1:2M, 0:4K
    output reg              PTWi_RDEN           //read a result
);
/***********************************************************************
    阅读这份代码前你需要知道的：
    * PTW： Page Table Walker ： 负责遍历页表的打工仔

************************************************************************/
//--------------TLB访问和写检查----------------------
    reg         TLB_access;                     //TLB access signal
    reg         Page_writeCHK, Page_readCHK, Page_eXeCHK;
    wire        page_checkOK;
//--------------交换表项----------------------------
    reg         TLB_replace;                    //TLB替换
    wire [9:0]  TLB_PTE;
    wire [43:0] TLB_PPN;
//---------------TLB miss信号-----------------------
    wire        TLB_miss;
    reg         ErrorPage_write;                //write to error page register
//-------------Error page-------------------
    reg         ErrorPageV;
    reg [26:0]  ErrorVPN;
    reg [7:0]   ErrorType;
//-----------------------------PTW工作标志位--------------------------
//  每次派遣一个查询操作到PTW后，都会在这里做记录，以确定从PTW返回的结果是当前需要的
    reg         PTW_working_clear;              //清除正在等待的项
    reg         PTW_working;                    //PTW 正在工作
    reg [3:0]   PTW_workingID;                  //PTW中正在进行的工作的ID
    reg [26:0]  PTW_WorkingVPN;                 //PTW中正在转换的VPN

always@(posedge TLBi_CLK or posedge TLBi_ARST)begin
    if(TLBi_ARST)begin
        PTW_working <= 1'b0;
    end
    else if(TLBi_pipflush)begin                 //流水线冲刷时，清除正在工作的标志位
        PTW_working <= 1'b0;
    end
    else if(PTWi_V)begin                        //当写入到PTW时，标志位置位
        PTW_working     <= 1'b1;
        PTW_WorkingVPN  <= PTWi_VPN;            //储存当前正在进行转换的VPN
    end
    else if(PTW_working_clear)begin             //如果从PTW返回队列中读出一项符合的，则清除当前正在等待的
        PTW_working     <= 1'b0;
        PTW_workingID   <= PTW_workingID + 'd1; //ID设置为下一次要使用的ID
    end
end
//---------------------------产生TLB查询和写检查信号---------------------
always@(*)begin
    if(TLBi_valid)begin                                                   //当前没有操作正在等待
        case(TLBi_Opcode)
            `LSU_eXecute:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b0;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b1;
            end
            `LSU_READ   :
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b0;
                Page_readCHK = 1'b1;
                Page_eXeCHK  = 1'b0;
            end
            `LSU_WRITE  :
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;
            end
            `LSU_READ_Lock:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b0;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0; 
            end
            `LSU_WRITE_Unloc:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;    
            end
            `LSU_AMOSWAP:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;    
            end
            `LSU_AMOADD:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0; 
            end
            `LSU_AMOXOR:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;    
            end
            `LSU_AMOAND:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;    
            end
            `LSU_AMOOR:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;      
            end
            `LSU_AMOMAX:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;     
            end
            `LSU_AMOMIN:
            begin
                TLB_access  = 1'b1;
                Page_writeCHK= 1'b1;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;     
            end
            default:
            begin
                TLB_access  = 1'b0;
                Page_writeCHK= 1'b0;
                Page_readCHK = 1'b0;
                Page_eXeCHK  = 1'b0;
            end
        endcase
    end
    else begin
        TLB_access  = 1'b0;
        Page_writeCHK= 1'b0;
        Page_readCHK = 1'b0;
        Page_eXeCHK  = 1'b0;
    end
end
//---------------------------产生PTW工作信号---------------------------
always@(*)begin
    if(!PTW_working & TLBi_ModifyPermit)begin                               //当前没有操作正在等待,且允许进行操作
        if(TLBo_InstPageFlt | TLBo_LoadPageFlt | TLBo_StorePageFlt)begin    //当前页面有页面错误，不进行PTW
            PTWi_V      = 1'b0;
            PTWi_VPN    = 'hx;
            PTWi_CMD    = `TLB_CMD_NOP;
            PTWi_WalkID = PTW_workingID;
        end
        else if(TLB_miss & !PTWo_FULL)begin          //当前TLB miss
            case(TLBi_Opcode)
                `LSU_eXecute:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_xLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_READ   :
                begin
                    PTWi_V      = 1'b1 ;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_rLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_WRITE  :
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_READ_Lock:
                begin
                    PTWi_V      = TLB_miss ? 1'b1 : 1'b0;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = TLB_miss ? `TLB_CMD_rLUT : `TLB_CMD_NOP;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_WRITE_Unloc:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_AMOSWAP:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_AMOADD:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_AMOXOR:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_AMOAND:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_AMOOR:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_AMOMAX:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                `LSU_AMOMIN:
                begin
                    PTWi_V      = 1'b1;
                    PTWi_VPN    = TLBi_DATA_VA[38:12];
                    PTWi_CMD    = `TLB_CMD_wLUT;
                    PTWi_WalkID = PTW_workingID;
                end
                default:
                begin
                    PTWi_V      = 1'b0;
                    PTWi_VPN    = 'hx;
                    PTWi_CMD    = 'hx;
                    PTWi_WalkID = PTW_workingID;
                end
            endcase
        end
        else begin
            PTWi_V      = 1'b0;
            PTWi_VPN    = 'hx;
            PTWi_CMD    = 'hx;
            PTWi_WalkID = PTW_workingID;
        end
    end
    else begin
        PTWi_V      = 1'b0;
        PTWi_VPN    = 'hx;
        PTWi_CMD    = 'hx;
        PTWi_WalkID = PTW_workingID;
    end
end
//---------------------------产生TLB交换表项信号------------------------
always@(*)begin
    if(PTWo_V)begin
        if(PTW_working & (PTWo_WalkID==PTW_workingID))begin    //当前从PTW返回的页表是正在等待的页表
            case(PTWo_RPL)
                `TLB_PRL_RDY :
                begin
                    PTWi_RDEN       = 1'b1;
                    TLB_replace     = (TLB_miss&(PTW_WorkingVPN==TLBi_DATA_VA[38:12])) ? 1'b1 : 1'b0;
                    ErrorPage_write = 1'b0;                        //当前不是错误页面
                end
                `TLB_RPL_xPERR:
                begin
                    PTWi_RDEN       = 1'b1;
                    TLB_replace     = 1'b0;
                    ErrorPage_write = 1'b1;
                end
                `TLB_RPL_rPERR:
                begin
                    PTWi_RDEN       = 1'b1;
                    TLB_replace     = 1'b0;
                    ErrorPage_write = 1'b1;
                end
                `TLB_RPL_wPERR:
                begin
                    PTWi_RDEN       = 1'b1;
                    TLB_replace     = 1'b0;
                    ErrorPage_write = 1'b1; 
                end
                default:
                begin
                    PTWi_RDEN       = 1'b1;
                    TLB_replace     = 1'b0;
                    ErrorPage_write = 1'b0;
                end
            endcase
        end
        else begin                          //返回的表项和当前所需要的表项无关，直接丢弃
            PTWi_RDEN       = 1'b1;
            TLB_replace     = 1'b0;
            ErrorPage_write = 1'b0;
        end
    end
    else begin                              //当前PTW无结果返回
        PTWi_RDEN       = 1'b0;
        TLB_replace     = 1'b0;
        ErrorPage_write = 1'b0;
    end
end
always@(*)begin
    if(PTWo_V & PTW_working & (PTWo_WalkID==PTW_workingID))begin    //当前读出的表项和目前正在等待的有关系
        PTW_working_clear = 1'b1;
    end
    else begin
        PTW_working_clear = 1'b0;
    end
end
//---------------------------Page Error寄存器---------------------------
//                Page Error 寄存器存储转换过程中出现了错误的页面
always@(posedge TLBi_CLK or posedge TLBi_ARST)begin
    if(TLBi_ARST)begin
        ErrorPageV<= 1'b0;
    end
    else if(TLBi_refersh)begin
        ErrorPageV<= 1'b0;
    end
    else if(ErrorPage_write)begin
        ErrorPageV<= 1'b1;
        ErrorVPN <= PTW_WorkingVPN;
        ErrorType<= PTWo_RPL;
    end
end

TLBcore#(.TLB_entry_NUM (TLB_entry_NUM))TLBcore(
//global signals
    .TLBi_CLK               (TLBi_CLK),               //Clock input
    .TLBi_ARST              (TLBi_ARST),              //Async reset input(Active High)
//control signals
    .TLBi_access            (TLB_access),            //access enable
    .TLBi_writeCHK          (Page_writeCHK),
    .TLBi_refersh           (TLBi_refersh),           //refersh the TLB
    .TLBi_replace           (TLB_replace),           //replace enable
//
    .TLBi_PPN               (PTWo_PPN),               //New PPN input
    .TLBi_PTE               (PTWo_PTE),               //New PTE input
    .TLBi_PageSize          (PTWo_PageSize),          //
    .TLBi_VPN               (TLBi_DATA_VA[38:12]),    //Virtual Page Number input
//
    .TLBo_PPN               (TLB_PPN),               //Physical Page Number output
    .TLBo_PTE               (TLB_PTE),               //Page Table output
    .TLBo_miss              (TLB_miss)               //TLB miss happen
);
PageCheck           ATU_PageCheck(
//csr
    .CSR_priv               (TLBi_privilege),      //CPU privlage input
    .CSR_mxr                (TLBi_CSR_mxr),            //CSR MXR bit input
    .CSR_sum                (TLBi_CSR_sum),            //CSR SUM bit input
//read or write
    .OP_read                (Page_readCHK),
    .OP_write               (Page_writeCHK),
    .OP_execute             (Page_eXeCHK),
    .PTE_U                  (TLB_PTE[`Sv39_U]),
    .PTE_W                  (TLB_PTE[`Sv39_W]),
    .PTE_R                  (TLB_PTE[`Sv39_R]),
    .PTE_X                  (TLB_PTE[`Sv39_X]),
    .PTE_D                  (TLB_PTE[`Sv39_D]),
    .check_ok               (page_checkOK)
);

//---------------------------output-----------------------------------
always@(*)begin
    if(TLBi_valid)begin
        if((TLBi_DATA_VA[38:12]==ErrorVPN) & ErrorPageV)begin       //当前页面和一个错误页面匹配
            TLBo_valid          = 1'b1;
            TLBo_LoadPageFlt    = (ErrorType==`TLB_RPL_rPERR);
            TLBo_StorePageFlt   = (ErrorType==`TLB_RPL_wPERR);
            TLBo_InstPageFlt    = (ErrorType==`TLB_RPL_xPERR);
            TLBo_DATA_PA        = 'h0;
            TLBo_DATA_Cacheable = 'b0;
            TLBo_DATA_WrThrough = 'b0;
        end
        else if(TLB_miss)begin              //当前页面没有在TLB中找到
            TLBo_valid          = 1'b0;
            TLBo_LoadPageFlt    = 1'b0;
            TLBo_StorePageFlt   = 1'b0;
            TLBo_InstPageFlt    = 1'b0;
            TLBo_DATA_PA        = 'h0;
            TLBo_DATA_Cacheable = 'b0;
            TLBo_DATA_WrThrough = 'b0;
        end
        else if(!page_checkOK)begin         //当前页面检查不通过
            TLBo_valid          = 1'b1;
            TLBo_LoadPageFlt    = Page_readCHK;
            TLBo_StorePageFlt   = Page_writeCHK;
            TLBo_InstPageFlt    = Page_eXeCHK;
            TLBo_DATA_PA        = 'h0;
            TLBo_DATA_Cacheable = 'b0;
            TLBo_DATA_WrThrough = 'b0;
        end
        else begin
            TLBo_valid          = 1'b1;
            TLBo_LoadPageFlt    = 1'b0;
            TLBo_StorePageFlt   = 1'b0;
            TLBo_InstPageFlt    = 1'b0;
            TLBo_DATA_PA        = {8'b0,TLB_PPN,TLBi_DATA_VA[11:0]};
            TLBo_DATA_Cacheable = 1'b0;
            TLBo_DATA_WrThrough = 1'b0; 
        end
    end
    else begin              //没有对TLB的访问
        TLBo_valid          = 1'b0;
        TLBo_LoadPageFlt    = 1'b0;
        TLBo_StorePageFlt   = 1'b0;
        TLBo_InstPageFlt    = 1'b0;
        TLBo_DATA_PA        = 'h0;
        TLBo_DATA_Cacheable = 'b0;
        TLBo_DATA_WrThrough = 'b0;
    end
end


endmodule





